package com.liming.auth.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liming.auth.mapper.MenuMapper;
import com.liming.auth.service.MenuService;
import com.liming.auth.service.RoleMenuService;
import com.liming.common.exception.BusinessException;
import com.liming.common.pojo.dto.authinfo.AddOrUpdateMenuDTO;
import com.liming.common.pojo.entity.BaseEntity;
import com.liming.common.pojo.entity.authinfo.Menu;
import com.liming.common.pojo.entity.authinfo.RoleMenu;
import com.liming.common.pojo.vo.authinfo.MenuVO;
import com.liming.common.util.mybatisplus.MPUtil;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;


/**
 * @author liming
 * @date 2025/2/16
 */
@Service
@AllArgsConstructor
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService, MPUtil<Menu> {

    private final RoleMenuService roleMenuService;

    @Override
    public List<MenuVO> tree() {
        // 1. 获取所有菜单列表
        // 按照排序字段升序
        List<Menu> menuList = this.list(getLQW().orderByAsc(Menu::getSort));
        // 2. 构建树形结构 | 0表示顶级菜单 | 返回结果
        return buildMenuTree(menuList, 0L);
    }

    @Override
    public List<Long> menuIdsByRoleId(Long id) {
        // 菜单IDS
        List<Long> menuIds = roleMenuService.menuIdsByRoleId(id);
        if (CollUtil.isEmpty(menuIds)) {
            return Collections.emptyList();
        }
        return menuIds;
    }

    private List<MenuVO> buildMenuTree(List<Menu> menuList, Long parentId) {
        return menuList.stream()
                .filter(menu -> menu.getParentId().equals(parentId))
                .map(menu -> {
                    MenuVO menuVO = new MenuVO();
                    BeanUtils.copyProperties(menu, menuVO);
                    // 递归获取子菜单
                    List<MenuVO> children = buildMenuTree(menuList, menu.getId());
                    if (!children.isEmpty()) {
                        menuVO.setChildren(children);
                    }
                    return menuVO;
                })
                .collect(Collectors.toList());
    }

    @Override
    public Boolean saveMenu(AddOrUpdateMenuDTO dto) {
        Menu save = new Menu();
        BeanUtils.copyProperties(dto, save);
        Assert.isTrue(save(save), () -> new BusinessException("菜单保存失败"));
        return true;
    }

    @Override
    public Boolean updateMenu(AddOrUpdateMenuDTO dto) {
        Menu update = new Menu();
        BeanUtils.copyProperties(dto, update);
        Assert.isTrue(updateById(update), () -> new BusinessException("菜单更新失败"));
        return true;
    }

    @Override
    @Transactional
    public Boolean removeMenuWithChildrenById(Long id) {
        // 删除角色与菜单之间的关联
        roleMenuService.removeByMenuId(id);
        // 删除菜单之间的关联
        // 删除子菜单
        lambdaUpdate().eq(Menu::getParentId, id).remove();
        // 删除当前菜单
        Assert.isTrue(removeById(id), () -> new BusinessException("菜单删除失败"));
        return true;
    }

    @Override
    @Transactional
    public Boolean updRoleMenu(Long id, List<Long> menuIds) {
        // 删除角色原本菜单权限
        roleMenuService.removeByRoleId(id);
        if (CollUtil.isEmpty(menuIds)) {
            return true;
        }
        List<RoleMenu> save = menuIds.stream().map(menuId -> {
            RoleMenu roleMenu = new RoleMenu();
            roleMenu.setRoleId(id);
            roleMenu.setMenuId(menuId);
            return roleMenu;
        }).toList();
        Assert.isTrue(roleMenuService.saveBatch(save), () -> new BusinessException("更新角色菜单权限失败"));
        return true;
    }

    @Override
    public List<MenuVO> menuByMenuIds(List<Long> menuIds) {
        List<Menu> menuList = lambdaQuery().in(BaseEntity::getId, menuIds).orderByAsc(Menu::getSort).list();
        return buildMenuTree(menuList, 0L);
    }
}
